using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using Extension = SharedObjects.Extension;

namespace TheGameDatabaseTool
{
	/// <summary>
	/// This class coordinates the population on the world map, based on a high res 2bit world map.
	/// It will find free spots on the map to automatically place computer systems on.
	/// There is code that will allow for grouping of systems based on their parent corporation, which is currently unused.
	/// Instead, a purely random placement on the map happens.
	/// </summary>
	public class CoordinateCoordinator
	{
		private const string MapFilePath = "..\\..\\Worldmap_2bit_highres.bmp";

		public int Width { get; private set; }
		public int Height { get; private set; }
		private Bitmap Bitmap { get; set; }
		private readonly Dictionary<PointD, ulong> _gridPoints = new Dictionary<PointD, ulong>();
		private readonly List<PointD> _occupiedCoordinates = new List<PointD>();
		private const double GridSpacing = 0.125 / 2.0; //128 px on 12800 resolution
		private double _minDistance = 0.025; //this is a field to be used to prevent points being too close to each other. Currently unused.
		private const double CorporationRadius = 0.75;
	
		public CoordinateCoordinator()
		{
			if(File.Exists(MapFilePath))
			{
				Image bmp = Bitmap.FromFile(MapFilePath);
				Width = bmp.Width;
				Height = bmp.Height;
				Bitmap = new Bitmap(bmp);
			}

			for(double y = GridSpacing; y < 100; y+=GridSpacing)
			{
				double shift = y%2 == 0 ? GridSpacing/2.0 : 0.0;
				for(double x = GridSpacing + shift; x < 100; x+=GridSpacing)
				{
					if (CanUsePercCoord(x, y))
						_gridPoints.Add(new PointD { X = x, Y = y }, Extension.ToLong(Guid.Empty));
				}
			}
		}

		public bool CanUsePercCoord(double x, double y)
		{
			Point imgCoord = GetPixelCoordinatesByPercentage(x, y);
			if (CanUseCoordinate(imgCoord.X, imgCoord.Y))
				return false;
			return true;
		}

		public PointD GetFreeRandomGridpoint(ulong corpId)
		{
			var unusedGridPoints = (from kvp in _gridPoints where kvp.Value == 0 select kvp.Key);
			//var usedGridPoints = (from kvp in gridPoints where kvp.Value != Guid.Empty select kvp.Key).ToList();
			Random rand = new Random(Environment.TickCount);

			bool isTooClose = true;
			PointD retVal = unusedGridPoints.ElementAt(rand.Next(0, unusedGridPoints.Count() - 1));
			//retVal = unusedGridPoints[rand.Next(0, unusedGridPoints.Count() - 1)];
	
			//while (isTooClose)
			//{
			//    isTooClose = false;
			//    foreach (PointD used in usedGridPoints)
			//    {
			//        if (GetPointDistance(used, retVal) < MinDistance*2)
			//        {
			//            isTooClose = true;
			//            break;
			//        }
			//    }
			//    if(isTooClose)
			//        retVal = unusedGridPoints[rand.Next(0, unusedGridPoints.Count() - 1)];
			//}
			_gridPoints[retVal] = corpId;
			return retVal;
		}

		public PointD GetFreeChildGridpoint(PointD parentPoint, ulong corpId, int count)
		{
			return GetFreeRandomGridpoint(corpId);
			//Guid storedID;
			//if (!gridPoints.TryGetValue(parentPoint, out storedID))
			//    return null;
			
			//if (storedID != corpId)
			//    return null;

			//circle around the parentpoint with distance 0.01%, and check if a point is A) not taken and B) occupiable
			//there are always 5 hosts for a corporation, thus we draw a 5-gon around parentPoint.
			//formula:
			//x = center.X + radius * cos(angle);
			//y = center.Y + radius * sin(angle);
			double stepSize = 360.0 / 10; //360 degrees by 5 points
			for(double i = count * stepSize; i < 360; i+= stepSize)
			{
				double rad = DegreeToRadian(i);
				double x = (Math.Cos(rad) * CorporationRadius) + parentPoint.X;
				double y = (Math.Sin(rad) * CorporationRadius) + parentPoint.Y;
				
				if(CanUsePercCoord(x, y)) //we're on land
				{
					PointD newPoint = new PointD(x,y);
					bool isTooClose = false;
					//foreach (PointD p in occupiedCoordinates)
					//{
					//    //if we find even one point, that's too close, we abort this iteration to save time.
					//    //Execution path will then continue looping thru the angles.
					//    if (GetPointDistance(p, newPoint) < MinDistance)
					//    {
					//        isTooClose = true;
					//        break;
					//    }
					//}
					if (!isTooClose)
					{
						_occupiedCoordinates.Add(newPoint);
						return newPoint;
					}
				}
			}
			return null;
		}

		bool CanUseCoordinate(int x, int y)
		{
			if (x > Bitmap.Width || y > Bitmap.Height) return false;

			if (Bitmap.GetPixel(x, y).ToArgb() == Color.White.ToArgb())
				return true;
			return false;
		}

		Point GetPixelCoordinatesByPercentage(double x, double y)
		{
			//100% = Width, percentage = X --> percentage * Width / 100;
			return new Point(
				(int)(x * Width / 100.0),
				(int)(y * Height / 100.0)
				);
		}

		double DegreeToRadian(double degrees)
		{
			return degrees*(Math.PI/180.0);
		}

		double RadianToDegree(double radian)
		{
			return radian * (180.0/Math.PI);
		}

		double GetPointDistance(PointD p1, PointD p2)
		{
			//formula:
			// Sqrt( (x2-x1)^2 + (y2-y1)^2 )
			return Math.Sqrt(Math.Pow(p2.X - p1.X, 2) + Math.Pow(p2.Y - p1.Y, 2));
		}
	}

	public class PointD
	{
		public double X { get; set; }
		public double Y { get; set; }

		public PointD(){}

		public PointD(double x, double y)
		{
			X = x;
			Y = y;
		}

		public override string ToString()
		{
			return string.Format("{0}, {1}", X, Y);
		}
	}
}